//! import "common\Base.j"

library DataSystem initializer Init requires Base

struct Table
    private static hashtable table

    static method create takes nothing returns Table
        local Table value = Table.allocate()
        return value
    endmethod

    private static method onInit takes nothing returns nothing
        set Table.table = InitHashtable()
    endmethod

    private method onDestroy takes nothing returns nothing
        call FlushChildHashtable(Table.table, this)
    endmethod

    method delete takes integer k returns nothing
        call RemoveSavedInteger(Table.table, this, k)
        call RemoveSavedReal(Table.table, this, k)
        call RemoveSavedBoolean(Table.table, this, k)
        call RemoveSavedString(Table.table, this, k)
        call RemoveSavedHandle(Table.table, this, k)
    endmethod

    method exist takes integer k returns boolean
        return HaveSavedInteger(Table.table, this, k) or HaveSavedReal(Table.table, this, k) or HaveSavedBoolean(Table.table, this, k) or HaveSavedString(Table.table, this, k) or HaveSavedHandle(Table.table, this, k)
    endmethod

    method writeInteger takes integer k, integer value returns nothing
        call RemoveSavedReal(Table.table, this, k)
        call RemoveSavedBoolean(Table.table, this, k)
        call RemoveSavedString(Table.table, this, k)
        call RemoveSavedHandle(Table.table, this, k)
        call SaveInteger(Table.table, this, k, value)
    endmethod

    method readInteger takes integer k returns integer
        return LoadInteger(Table.table, this, k)
    endmethod

    method writeReal takes integer k, real value returns nothing
        call RemoveSavedInteger(Table.table, this, k)
        call RemoveSavedBoolean(Table.table, this, k)
        call RemoveSavedString(Table.table, this, k)
        call RemoveSavedHandle(Table.table, this, k)
        call SaveReal(Table.table, this, k, value)
    endmethod

    method readReal takes integer k returns real
        return LoadReal(Table.table, this, k)
    endmethod

    method writeBoolean takes integer k, boolean value returns nothing
        call RemoveSavedInteger(Table.table, this, k)
        call RemoveSavedReal(Table.table, this, k)
        call RemoveSavedString(Table.table, this, k)
        call RemoveSavedHandle(Table.table, this, k)
        call SaveBoolean(Table.table, this, k, value)
    endmethod

    method readBoolean takes integer k returns boolean
        return LoadBoolean(Table.table, this, k)
    endmethod

    method writeString takes integer k, string value returns nothing
        call RemoveSavedInteger(Table.table, this, k)
        call RemoveSavedReal(Table.table, this, k)
        call RemoveSavedBoolean(Table.table, this, k)
        call RemoveSavedHandle(Table.table, this, k)
        call SaveStr(Table.table, this, k, value)
    endmethod

    method readString takes integer k returns string
        return LoadStr(Table.table, this, k)
    endmethod

    //! textmacro DataSystemTable takes name, origin, type
    method write$name$ takes integer k, $type$ value returns nothing
        call RemoveSavedInteger(Table.table, this, k)
        call RemoveSavedReal(Table.table, this, k)
        call RemoveSavedBoolean(Table.table, this, k)
        call RemoveSavedString(Table.table, this, k)
        call Save$origin$Handle(Table.table, this, k, value)
    endmethod

    method read$name$ takes integer k returns $type$
        return Load$origin$Handle(Table.table, this, k)
    endmethod
    //! endtextmacro

    //! runtextmacro DataSystemTable("player", "Player", "player")
    //! runtextmacro DataSystemTable("widget", "Widget", "widget")
    //! runtextmacro DataSystemTable("destructable", "Destructable", "destructable")
    //! runtextmacro DataSystemTable("item", "Item", "item")
    //! runtextmacro DataSystemTable("unit", "Unit", "unit")
    //! runtextmacro DataSystemTable("ability", "Ability", "ability")
    //! runtextmacro DataSystemTable("timer", "Timer", "timer")
    //! runtextmacro DataSystemTable("trigger", "Trigger", "trigger")
    //! runtextmacro DataSystemTable("triggercondition", "TriggerCondition", "triggercondition")
    //! runtextmacro DataSystemTable("triggeraction", "TriggerAction", "triggeraction")
    //! runtextmacro DataSystemTable("event", "TriggerEvent", "event")
    //! runtextmacro DataSystemTable("force", "Force", "force")
    //! runtextmacro DataSystemTable("group", "Group", "group")
    //! runtextmacro DataSystemTable("location", "Location", "location")
    //! runtextmacro DataSystemTable("rect", "Rect", "rect")
    //! runtextmacro DataSystemTable("boolexpr", "BooleanExpr", "boolexpr")
    //! runtextmacro DataSystemTable("sound", "Sound", "sound")
    //! runtextmacro DataSystemTable("effect", "Effect", "effect")
    //! runtextmacro DataSystemTable("unitpool", "UnitPool", "unitpool")
    //! runtextmacro DataSystemTable("itempool", "ItemPool", "itempool")
    //! runtextmacro DataSystemTable("quest", "Quest", "quest")
    //! runtextmacro DataSystemTable("questitem", "QuestItem", "questitem")
    //! runtextmacro DataSystemTable("defeatcondition", "DefeatCondition", "defeatcondition")
    //! runtextmacro DataSystemTable("timerdialog", "TimerDialog", "timerdialog")
    //! runtextmacro DataSystemTable("leaderboard", "Leaderboard", "leaderboard")
    //! runtextmacro DataSystemTable("multiboard", "Multiboard", "multiboard")
    //! runtextmacro DataSystemTable("multiboarditem", "MultiboardItem", "multiboarditem")
    //! runtextmacro DataSystemTable("trackable", "Trackable", "trackable")
    //! runtextmacro DataSystemTable("dialog", "Dialog", "dialog")
    //! runtextmacro DataSystemTable("button", "Button", "button")
    //! runtextmacro DataSystemTable("texttag", "TextTag", "texttag")
    //! runtextmacro DataSystemTable("lightning", "Lightning", "lightning")
    //! runtextmacro DataSystemTable("image", "Image", "image")
    //! runtextmacro DataSystemTable("ubersplat", "Ubersplat", "ubersplat")
    //! runtextmacro DataSystemTable("region", "Region", "region")
    //! runtextmacro DataSystemTable("fogstate", "FogState", "fogstate")
    //! runtextmacro DataSystemTable("fogmodifier", "FogModifier", "fogmodifier")
endstruct

globals
    private hashtable table
endglobals

function Clear takes handle h returns nothing
    call FlushChildHashtable(table, GetHandleId(h))
endfunction

function Delete takes handle h, integer k returns nothing
    local integer index = GetHandleId(h)
    call RemoveSavedInteger(table, index, k)
    call RemoveSavedReal(table, index, k)
    call RemoveSavedBoolean(table, index, k)
    call RemoveSavedString(table, index, k)
    call RemoveSavedHandle(table, index, k)
endfunction

function Exist takes handle h, integer k returns boolean
    local integer index = GetHandleId(h)
    return HaveSavedInteger(table, index, k) or HaveSavedReal(table, index, k) or HaveSavedBoolean(table, index, k) or HaveSavedString(table, index, k) or HaveSavedHandle(table, index, k)
endfunction

function WriteInteger takes handle h, integer k, integer value returns nothing
    local integer index = GetHandleId(h)
    call RemoveSavedReal(table, index, k)
    call RemoveSavedBoolean(table, index, k)
    call RemoveSavedString(table, index, k)
    call RemoveSavedHandle(table, index, k)
    call SaveInteger(table, index, k, value)
endfunction

function ReadInteger takes handle h, integer k returns integer
    return LoadInteger(table, GetHandleId(h), k)
endfunction

function WriteReal takes handle h, integer k, real value returns nothing
    local integer index = GetHandleId(h)
    call RemoveSavedInteger(table, index, k)
    call RemoveSavedBoolean(table, index, k)
    call RemoveSavedString(table, index, k)
    call RemoveSavedHandle(table, index, k)
    call SaveReal(table, index, k, value)
endfunction

function ReadReal takes handle h, integer k returns real
    return LoadReal(table, GetHandleId(h), k)
endfunction

function WriteBoolean takes handle h, integer k, boolean value returns nothing
    local integer index = GetHandleId(h)
    call RemoveSavedInteger(table, index, k)
    call RemoveSavedReal(table, index, k)
    call RemoveSavedString(table, index, k)
    call RemoveSavedHandle(table, index, k)
    call SaveBoolean(table, index, k, value)
endfunction

function ReadBoolean takes handle h, integer k returns boolean
    return LoadBoolean(table, GetHandleId(h), k)
endfunction

function WriteString takes handle h, integer k, string value returns nothing
    local integer index = GetHandleId(h)
    call RemoveSavedInteger(table, index, k)
    call RemoveSavedReal(table, index, k)
    call RemoveSavedBoolean(table, index, k)
    call RemoveSavedHandle(table, index, k)
    call SaveStr(table, index, k, value)
endfunction

function ReadString takes handle h, integer k returns string
    return LoadStr(table, GetHandleId(h), k)
endfunction

//! textmacro DataSystemHandle takes name, origin, type
function Write$name$ takes handle h, integer k, $type$ value returns nothing
    local integer index = GetHandleId(h)
    call RemoveSavedInteger(table, index, k)
    call RemoveSavedReal(table, index, k)
    call RemoveSavedBoolean(table, index, k)
    call RemoveSavedString(table, index, k)
    call Save$origin$Handle(table, index, k, value)
endfunction

function Read$name$ takes handle h, integer k returns $type$
    return Load$origin$Handle(table, GetHandleId(h), k)
endfunction
//! endtextmacro

//! runtextmacro DataSystemHandle("Player", "Player", "player")
//! runtextmacro DataSystemHandle("Widget", "Widget", "widget")
//! runtextmacro DataSystemHandle("Destructable", "Destructable", "destructable")
//! runtextmacro DataSystemHandle("Item", "Item", "item")
//! runtextmacro DataSystemHandle("Unit", "Unit", "unit")
//! runtextmacro DataSystemHandle("Ability", "Ability", "ability")
//! runtextmacro DataSystemHandle("Timer", "Timer", "timer")
//! runtextmacro DataSystemHandle("Trigger", "Trigger", "trigger")
//! runtextmacro DataSystemHandle("TriggerCondition", "TriggerCondition", "triggercondition")
//! runtextmacro DataSystemHandle("TriggerAction", "TriggerAction", "triggeraction")
//! runtextmacro DataSystemHandle("Event", "TriggerEvent", "event")
//! runtextmacro DataSystemHandle("Force", "Force", "force")
//! runtextmacro DataSystemHandle("Group", "Group", "group")
//! runtextmacro DataSystemHandle("Location", "Location", "location")
//! runtextmacro DataSystemHandle("Rect", "Rect", "rect")
//! runtextmacro DataSystemHandle("BooleanExpr", "BooleanExpr", "boolexpr")
//! runtextmacro DataSystemHandle("Sound", "Sound", "sound")
//! runtextmacro DataSystemHandle("Effect", "Effect", "effect")
//! runtextmacro DataSystemHandle("UnitPool", "UnitPool", "unitpool")
//! runtextmacro DataSystemHandle("ItemPool", "ItemPool", "itempool")
//! runtextmacro DataSystemHandle("Quest", "Quest", "quest")
//! runtextmacro DataSystemHandle("QuestItem", "QuestItem", "questitem")
//! runtextmacro DataSystemHandle("DefeatCondition", "DefeatCondition", "defeatcondition")
//! runtextmacro DataSystemHandle("TimerDialog", "TimerDialog", "timerdialog")
//! runtextmacro DataSystemHandle("Leaderboard", "Leaderboard", "leaderboard")
//! runtextmacro DataSystemHandle("Multiboard", "Multiboard", "multiboard")
//! runtextmacro DataSystemHandle("MultiboardItem", "MultiboardItem", "multiboarditem")
//! runtextmacro DataSystemHandle("Trackable", "Trackable", "trackable")
//! runtextmacro DataSystemHandle("Dialog", "Dialog", "dialog")
//! runtextmacro DataSystemHandle("Button", "Button", "button")
//! runtextmacro DataSystemHandle("TextTag", "TextTag", "texttag")
//! runtextmacro DataSystemHandle("Lightning", "Lightning", "lightning")
//! runtextmacro DataSystemHandle("Image", "Image", "image")
//! runtextmacro DataSystemHandle("Ubersplat", "Ubersplat", "ubersplat")
//! runtextmacro DataSystemHandle("Region", "Region", "region")
//! runtextmacro DataSystemHandle("FogState", "FogState", "fogstate")
//! runtextmacro DataSystemHandle("FogModifier", "FogModifier", "fogmodifier")

private function OnRemoveUnit takes unit u returns nothing
    call Clear(u)
endfunction

hook RemoveUnit OnRemoveUnit

globals
    private integer hash
    private integer array heads
    private unit array units
    private integer array nexts
    private integer freelist = 0
    private integer top = 1
endglobals

private function New takes nothing returns integer
    local integer result
    if freelist == 0 then
        set result = top
        set top = top + 1
    else
        set result = freelist
        set freelist = nexts[freelist]
    endif
    set nexts[result] = 0
    return result
endfunction

private function Free takes integer node returns nothing
    set nexts[node] = freelist
    set freelist = node
endfunction

private function Next takes nothing returns nothing
    local timer tm = GetExpiredTimer()
    local integer index = ReadInteger(tm, hash)
    local integer forward = heads[index]
    local integer current
    local integer next
    loop
        set current = nexts[forward]
        exitwhen current == 0
        set nexts[forward] = nexts[current]
        if GetUnitTypeId(units[current]) == 0 then
            call Clear(units[current])
        elseif not UnitAlive(units[current]) then
            set next = nexts[heads[index + 1]]
            set nexts[heads[index + 1]] = New()
            set units[nexts[heads[index + 1]]] = units[current]
            set nexts[nexts[heads[index + 1]]] = next
        endif
        set units[current] = null
        call Free(current)
    endloop
    set tm = null
endfunction

private function End takes nothing returns nothing
    local timer tm = GetExpiredTimer()
    local integer index = ReadInteger(tm, hash)
    local integer cursor = heads[index]
    local integer current
    local integer next
    loop
        set current = nexts[cursor]
        exitwhen current == 0
        if GetUnitTypeId(units[current]) == 0 then
            call Clear(units[current])
            set nexts[cursor] = nexts[current]
            set units[current] = null
            call Free(current)
        elseif UnitAlive(units[current]) then
            set nexts[cursor] = nexts[current]
            set units[current] = null
            call Free(current)
        else
            set cursor = current
        endif
    endloop
    set tm = null
endfunction

private function Start takes integer index, real second, code handler returns nothing
    local timer t = CreateTimer()
    call WriteInteger(t, hash, index)
    set heads[index] = New()
    call TimerStart(t, second, true, handler)
    set t = null
endfunction

private function OnDeath takes nothing returns boolean
    local unit localunit = GetTriggerUnit()
    local integer next
    if IsUnitIllusion(localunit) then
        call Clear(localunit)
    elseif not IsUnitType(localunit, UNIT_TYPE_HERO) then
        set next = nexts[heads[0]]
        set nexts[heads[0]] = New()
        set nexts[nexts[heads[0]]] = next
        set units[nexts[heads[0]]] = localunit
    endif
    set localunit = null
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger trg
    set table = InitHashtable()
    set hash = StringHash("index")
    set trg = CreateTrigger()
    call TriggerAddCondition(trg, Condition(function OnDeath))
    call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_DEATH)
    call Start(0, 5, function Next)
    call Start(1, 11, function Next)
    call Start(2, 31, function Next)
    call Start(3, 59, function Next)
    call Start(4, 127, function End)
endfunction

endlibrary